<?php
/**
 * 云极短信验证码服务 https://market.juncdt.com/details/id/19
 * hso
 * 2021/04/23
 */

namespace App\Services\Tools;

use App\Repositories\LogRepository;
use Carbon\Carbon;
use App\Exceptions\BasicException;
use Illuminate\Support\Facades\Log;
use Overtrue\EasySms\Exceptions\Exception;

class SmsYunjiService {

    const SMS_API_URL = 'https://market.face.juncdt.com/smartmarket/service/sendMessageToMultiple';
    const SUCCESS_CODE = 10000;
    private static $_error_msg = '';
    private static $_error_code = '';

    public static function getConfig(){
        return config('sms')['yunji']??[];
    }

    private static function getSign($data=[]){
        ksort($data);
        $str = '';
        foreach ($data as $k=>$v){
            $v = is_array($v)?json_encode($v):$v;
            $str .= "$k={$v}&";
        }
        $str = rtrim($str,'&');
        $lower256 = strtolower(hash("sha256", $str));
        $config = self::getConfig();
        $appSecret = $config['accessSecret']??'';
        //获取hmachsha1签名
        $hmasha = base64_encode(hash_hmac("sha1", $lower256, $appSecret, true));
        return $hmasha;
    }

    /**
     * @param $mobile string 手机号码
     * @param $type int 类型 1注册 2找回密码 3验证旧手机 4.绑定新手机 5.设置二级密码 6.绑定银行卡 7.找回qq账号 8.注销账号 9.快捷认证(支付宝身份验证) 10.人工认证 11.管理员通知
     * @param array $param 参数
     * @param int $limit 单位:分钟
     * @return string
     * @throws BasicException
     * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
     * @throws \Overtrue\EasySms\Exceptions\NoGatewayAvailableException
     */
    public static function send($mobile, $type, $param = [], $limit = 1,$template_id="") {
        $key = 'sms:' . $mobile . ':' . $type;
        if ($limit > 0 && app('redis')->get($key)) {
            throw new BasicException(0, $limit . '分钟可获取一次');
        }
        $sms_code = isset($param['code'])?$param['code']:self::getRandCode();
        // 测试 正式关闭
//        $mobileArr = [
//          '15575557558','16620431050'
//        ];
        if (env('SMS_REAL_SEND')) {
//        if (env('SMS_REAL_SEND') || in_array($mobile,$mobileArr)) {
            try {
                $curl_res = self::send_code($mobile, $type, $param,$sms_code,$template_id );
                if(!$curl_res){
                    $error_msg =  self::$_error_msg?self::$_error_msg:'发送失败，请重试';
                    Log::error($error_msg);
                    throw new BasicException(0, $error_msg);
                }
            } catch (Exception $e) {
                $error_msg =  $e->getMessage()??'';
                Log::error($error_msg);
                throw new BasicException(0, $error_msg);
            }
        }

        app('model_sms_history')->create([
            'mobile'   => $mobile,
            'code'     => $sms_code,
            'add_time' => Carbon::now()->timestamp,
            'type'     => $type
        ]);

        app('redis')->setex($key, $limit*60, $sms_code);
        return true;
    }
    private static function getRandCode(){
        return mt_rand(1000, 9999);
    }

    private static function send_code($mobile, $type, $param=[],$sms_code='',$template_id="" ){

        $config = self::getConfig();
        if(empty($config)){
            throw new BasicException(0,'配置错误,发放失败');
        }
        $sms_code = $sms_code?$sms_code:self::getRandCode();
        if($type == 1 && !empty($template_id)){
            $templateCode = self::getTemplateCode(100);
        }else{
            $templateCode = self::getTemplateCode($type);
        }

        if(empty($templateCode)){
            throw new BasicException(0,'对应发送短信类型不存在');
        }
        $param['code'] = $sms_code;
        $send_data = [
            "phone" => [$mobile],
            "accessKey" => $config['accessKey']??'',
            "accessSecret" => $config['accessSecret']??'',
            "classificationSecret" =>  $config['classificationSecret']??'',
            "templateCode" => $templateCode,
            "params" => $param,
            //"signCode" => $config['signCode']??'',
            "signCode" => $config['signCode']??'',
            "taskId" => $param['task_id']??'taskId'
        ];

        $sign = self::getSign($send_data);
        $send_data['sign'] = $sign;
        $logRtx = new LogRepository("sms-send");
        $logRtx->log("send_data",$send_data);
        $logRtx->File();
        return self::curl($send_data);
    }

    public static function getTemplateCode($type){
        $config = self::getConfig();
        // case 3:绑定手机号 4:绑定手机号  5: //设置二级密码  6: //绑定银行卡  7: //找回QQ账号 8.注销账号 9.快捷认证(支付宝身份验证) 10.人工认证
        if(in_array($type,[3,4,5,6,7,8,9,10])){
            $type = 3;
        }
        return $config['templateCode'.$type]??'';
    }

    public static function getErrorMsg(){
        return [
            'error_code' => self::$_error_code,
            'error_msg' => self::$_error_msg,
        ];
    }

    private static function curl($data){
        //curl请求
        $ch = curl_init(self::SMS_API_URL); //请求的URL地址
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));//$data JSON类型字符串
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Content-Length: ' . strlen(json_encode($data))));
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $output = curl_exec($ch);
        if($output === false){
            //请求失败，打印失败原因
            self::$_error_msg = 'curl_error_'.curl_error($ch);
            self::$_error_code= curl_errno($ch);
            return false;
        }
        curl_close($ch);
        $logRtx = new LogRepository("sms-send");
        $logRtx->log("output",json_decode($output, true, 512, JSON_THROW_ON_ERROR));
        $logRtx->File();
        $output = json_decode($output,true);
        $verify_code = $output['BusinessException']['message']['verifyCode']??'';
        if($verify_code!=self::SUCCESS_CODE){
            self::$_error_msg = $output['BusinessException']['message']['resultMessage']??'';
            self::$_error_code = $verify_code;
            return false;
        }
        return true;
    }

    //给风控管理员发预警短信
    public static function sentryToAdmin($code)
    {
        if (env('SMS_REAL_SEND')) {
            try {
                self::send_code('18655195019',11,[],$code);
            } catch (Exception $exception) {
                $error_msg = $exception->getMessage() ?? '发送失败，请重试';
                Log::error($error_msg);
                throw new BasicException(0, $error_msg);
            }
        }
    }


    //用户账务异常风控报警
    public static function sentryToAdminByAccount($code)
    {
        if (env('SMS_REAL_SEND')) {
            try {
                self::send_code('18655195019',11,[],$code);
            } catch (Exception $exception) {
                $error_msg = $exception->getMessage() ?? '发送失败，请重试';
                Log::error($error_msg);
                throw new BasicException(0, $error_msg);
            }
        }
    }

    public static function verify($mobile, $code, $type) {
        $where = [
            'mobile' => $mobile,
            'type' => $type,
            'status' => 0,
        ];
        $data = app('model_sms_history')->where($where)->orderBy('id', 'desc')->first();
        $result = false;
        $key = 'u:login_error_times:'.$mobile.':'.$type;

        if (isset($data) && $data->status == 0 && $data->code == $code) {
            if ($data->add_time < (Carbon::now()->timestamp - 1800)) {
                $status = 2;
                $result = false;
                self::$_error_msg = '验证码已过期';
            } else {
                $status = 1;
                $result = true;
            }
            app('model_sms_history')->where(['id' => $data->id, 'status' => 0])->update([
                'status'      => $status,
                'update_time' => Carbon::now()->timestamp
            ]);

        }
        //记录错误次数
        if(!$result){
            self::$_error_msg = '验证码错误';
            $times = app('redis')->incr($key);
            if ($times >= 5) {
                app('model_sms_history')->where($where)->update([
                    'status'      => 2,
                    'update_time' => Carbon::now()->timestamp
                ]);
                self::$_error_msg = '验证码输入错误次数过多，已失效，请重新获取';
                app('redis')->del($key);
                return false;
            } elseif ($times == 1) {
                app('redis')->expire($key,strtotime(date('Ymd')) + 86400 - time());
            }
        }
        return $result;
    }

}
